﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows.Input;
using WinForm = System.Windows.Forms;
using System.Windows.Media;
using System.Text.RegularExpressions;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// 创建者：杨震宇
    /// 创建时间：2013年3月29日
    /// 
    /// 说明：用作文档页面。
    /// </summary>
    public class MarkdownEditor : BetterWpfControls.TabItem
    {
        /// <summary>
        /// 需要添加“加密解锁器”。
        /// </summary>
        private Grid baseGrid = new Grid();

        /// <summary>
        /// 密码设置面板
        /// </summary>
        private SetPasswordPanel setPasswordPanel;

        private InputPasswordPanel inputPasswordPanel;
        /// <summary>
        /// 密码输入面板
        /// </summary>
        public InputPasswordPanel InputPasswordPanel
        {
            get
            {
                return inputPasswordPanel;
            }
        }

        private MarkDownEditorBase editorBase = new MarkDownEditorBase();
        /// <summary>
        /// 主编辑器。
        /// </summary>
        public MarkDownEditorBase EditorBase { get { return this.editorBase; } }

        private string fullFilePath = null;
        /// <summary>
        /// 文件路径。保存时使用。
        /// </summary>
        public string FullFilePath
        {
            get { return this.fullFilePath; }
            set
            {
                this.fullFilePath = value;

                if (string.IsNullOrEmpty(this.fullFilePath) || File.Exists(this.fullFilePath) == false)
                {
                    this.shortFileName = "unnamed.md";
                    this.headerPanel.ToolTip = null;
                }
                else
                {
                    int lastIndex = this.fullFilePath.LastIndexOf("\\");
                    if (lastIndex >= 0)
                    {
                        string name = this.fullFilePath.Substring(lastIndex + 1);
                        if (string.IsNullOrEmpty(name))
                        {
                            this.shortFileName = "unnamed.md";
                        }
                        else this.shortFileName = name;
                    }
                    else
                    {
                        this.shortFileName = "unnamed.md";
                    }

                    this.headerPanel.ToolTip = this.fullFilePath;
                }
                RefreshTitle();

                if (ShortFileName.StartsWith("_"))
                {
                    this.icon.Source = WorkspaceTreeViewItem.FolderDocumentImage;
                }
                else
                {
                    this.icon.Source = WorkspaceTreeViewItem.FileBitmapImage;
                }
            }
        }

        /// <summary>
        /// 没有标题就显示文件短名，有就显示标题。
        /// 标题是指 Markdown 文件中第一个以%开头的行的文本。
        /// </summary>
        private void RefreshTitle()
        {
            var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
            if (string.IsNullOrWhiteSpace(title) == false)
            {
                this.headerTextBlock.Text = title;
            }
            else
            {
                this.headerTextBlock.Text = this.shortFileName;
            }
        }

        private string shortFileName;
        /// <summary>
        /// 取文件路径中最后一个反斜杠之后的部分。如果没有，返回“ex”这两个字符。
        /// </summary>
        public string ShortFileName
        {
            get
            {
                return this.shortFileName;
            }
            set
            {
                this.shortFileName = value;
                var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
                if (string.IsNullOrWhiteSpace(title) == false)
                {
                    this.headerTextBlock.Text = title;
                }
                else
                {
                    this.headerTextBlock.Text = this.shortFileName;
                }
            }
        }

        private ContextMenu headerContextMenu = new ContextMenu();

        private ContextMenu editorContextMenu = new ContextMenu();

        /// <summary>
        /// 直接返回Editor.IsModified的值。
        /// </summary>
        public bool IsModified
        {
            get { return this.editorBase.IsModified; }
        }

        /// <summary>
        /// 决定是否将此编辑器“钉”在主选项卡左侧。
        /// </summary>
        private MenuItem miPin;

        private PresentationWindow presentationWindow = null;
        /// <summary>
        /// 试题演示窗口，考虑到再次打开时应仍然保持试题状态的必要性，每个编辑器都应有一个窗口实例。
        /// </summary>
        public PresentationWindow PresentationWindow
        {
            get
            {
                if (this.presentationWindow == null)
                {
                    this.presentationWindow = new PresentationWindow(this)
                    {
                        Owner = App.Current.MainWindow,
                        WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
                        ContentText = this.editorBase.Text,
                    };
                }
                return this.presentationWindow;
            }
        }

        /// <summary>
        /// [构造方法]创建一个 Markdown 编辑器。
        /// </summary>
        /// <param name="shortFileName">文件短名，用以显示在编辑器的标头上。</param>
        /// <param name="isExamEnabled">是否启用试题编辑功能。（依赖“自动完成”）</param>
        /// <param name="isAutoCompletionEnabled">是否启用“自动完成”功能。</param>
        /// <param name="isEnToChineseDictEnabled">是否启用英译中自动提示与完成功能。（依赖“自动完成”）</param>
        /// <param name="isShowSpaces">是否显示空格。</param>
        /// <param name="isShowEndOfLine">是否显示段落标记。</param>
        /// <param name="isShowTabs">是否显示 Tab 符。</param>
        /// <param name="isCompareAreaEditor">是否对照区。如果是对照区，双击标题头不能预览。</param>
        public MarkdownEditor(string shortFileName, bool isExamEnabled,
            bool isAutoCompletionEnabled, bool isEnToChineseDictEnabled,
            bool isShowSpaces, bool isShowEndOfLine, bool isShowTabs, bool isCompareAreaEditor)
        {
            this.isCompareAreaEditor = isCompareAreaEditor;
            this.Style = App.Current.FindResource("BetterTabItemStyle") as Style;
            this.ShortFileName = shortFileName;

            #region 初始化标题上下文菜单。
            this.headerPanel.ContextMenu = this.headerContextMenu;
            this.headerPanel.ContextMenu.Style = null;

            miPin = new MenuItem() { Header = "锁定在左侧(_L)", Style = null, };
            miPin.Click += miPin_Click;

            this.headerContextMenu.Items.Add(miPin);

            MenuItem miCloseDocument = new MenuItem() { Header = "关闭此文档(_C)", InputGestureText = "Ctrl+W", Style = null, };
            miCloseDocument.Click += miCloseDocument_Click;
            this.headerContextMenu.Items.Add(miCloseDocument);

            this.headerContextMenu.Items.Add(new Separator());

            MenuItem miCompileAndPreview = new MenuItem() { Header = "编译并预览Html文档(_P)", InputGestureText = "F5", Style = null, };
            miCompileAndPreview.Click += miCompileAndPreview_Click;
            this.headerContextMenu.Items.Add(miCompileAndPreview);

            MenuItem miCallSystemDefaultExplorerOpen = new MenuItem()
            {
                Header = "在外部预览(_E)",
                ToolTip = "先编译，再调用操作系统默认浏览器预览",
                Style = null,
            };

            miCallSystemDefaultExplorerOpen.Click += MiCallSystemDefaultWebBrowserPreview_Click;
            this.headerContextMenu.Items.Add(miCallSystemDefaultExplorerOpen);

            this.headerContextMenu.Items.Add(new Separator());

            MenuItem miSendToCompareArea = new MenuItem()
            {
                Header = "发送到对照区(_S)",
                Style = null,
            };
            miSendToCompareArea.Click += MiSendToCompareArea_Click;
            this.headerContextMenu.Items.Add(miSendToCompareArea);

            this.headerContextMenu.Opened += HeaderContextMenu_Opened;
            #endregion

            #region 初始化编辑区上下文菜单
            this.editorBase.ContextMenu = this.editorContextMenu;
            this.editorBase.ContextMenu.Style = null;

            MenuItem miUndo = new MenuItem()
            {
                Header = "撤销(_U)",
                InputGestureText = "Ctrl+Z",
                Style = null,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_UndoHS.png")),
                },
            };
            miUndo.Click += MiUndo_Click;
            this.editorContextMenu.Items.Add(miUndo);

            MenuItem miRedo = new MenuItem()
            {
                Header = "重做(_R)",
                InputGestureText = "Ctrl+Y",
                Style = null,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_RedoHS.png")),
                },
            };
            miRedo.Click += MiRedo_Click;
            this.editorContextMenu.Items.Add(miRedo);

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miCut = new MenuItem()
            {
                Header = "剪切(_X)",
                InputGestureText = "Ctrl+X",
                Style = null,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CutHS.png")),
                },
            };
            miCut.Click += MiCut_Click;
            this.editorContextMenu.Items.Add(miCut);

            MenuItem miCopy = new MenuItem()
            {
                Header = "复制(_C)",
                InputGestureText = "Ctrl+C",
                Style = null,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CopyHS.png")),
                },
            };
            miCopy.Click += MiCopy_Click;
            this.editorContextMenu.Items.Add(miCopy);

            MenuItem miPaste = new MenuItem()
            {
                Header = "粘贴(_P)",
                InputGestureText = "Ctrl+V",
                Style = null,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/PasteHS.png")),
                },
            };
            miPaste.Click += MiPaste_Click;
            this.editorContextMenu.Items.Add(miPaste);

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miFindLinkImage = new MenuItem()
            {
                Header = "预览引用的图像(_P)",
                InputGestureText = "Ctrl+G",
                Style = null,
                SnapsToDevicePixels = true,
            };
            miFindLinkImage.Click += MiFindLinkImage_Click;
            this.editorContextMenu.Items.Add(miFindLinkImage);

            MenuItem miOpenLinkedMarkdownFile = new MenuItem()
            {
                Header = "打开引用的 Markdown 文档(_M)",
                InputGestureText = "Ctrl+G",
                Style = null,
                SnapsToDevicePixels = true,
            };
            miOpenLinkedMarkdownFile.Click += MiOpenLinkedMarkdownFile_Click;
            this.editorContextMenu.Items.Add(miOpenLinkedMarkdownFile);

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miSimpleEncrypt = new MenuItem()
            {
                Header = "设置密码(_E)",
                Style = null,
                ToolTip = "只支持简单加密，勿用于高要求场合！",
                SnapsToDevicePixels = true,
            };
            miSimpleEncrypt.Click += MiSimpleEncrypt_Click;
            this.editorContextMenu.Items.Add(miSimpleEncrypt);

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miCollapseCurrentFolder = new MenuItem()
            {
                Header = "折叠/展开当前区块(_B)",
                InputGestureText = "Ctrl+Alt+Z",
                SnapsToDevicePixels = true,
            };
            miCollapseCurrentFolder.Click += MiCollapseCurrentFolder_Click;
            this.editorContextMenu.Items.Add(miCollapseCurrentFolder);

            MenuItem miCollapseAllBlocks = new MenuItem()
            {
                Header = "全部折叠(_A)",
                SnapsToDevicePixels = true,
            };
            miCollapseAllBlocks.Click += MiCollapseAllBlocks_Click;
            this.editorContextMenu.Items.Add(miCollapseAllBlocks);

            MenuItem miExpandAllBlocks = new MenuItem()
            {
                Header = "全部展开(_E)",
                SnapsToDevicePixels = true,
            };
            miExpandAllBlocks.Click += MiExpandAllBlocks_Click;
            this.editorContextMenu.Items.Add(miExpandAllBlocks);



            #endregion

            this.headerPanel.Children.Add(icon);
            this.headerPanel.Children.Add(this.modifiedTextBlock);
            this.headerPanel.Children.Add(this.headerTextBlock);
            this.closeImage.Source = closeImageSource;
            this.closeImage.MouseLeftButtonDown += closeImage_MouseLeftButtonDown;
            this.closeBorder.Child = this.closeImage;
            this.headerPanel.Children.Add(this.closeBorder);
            this.Header = this.headerPanel;

            this.editorBase.Document.UndoStack.PropertyChanged += UndoStack_PropertyChanged;

            this.headerTextBlock.MouseLeftButtonDown += HeaderTextBlock_MouseLeftButtonDown;

            //处理拖动
            this.AllowDrop = true;
            this.Drop += MarkdownEditor_Drop;

            this.closeBorder.MouseLeave += closeBorder_MouseLeave;
            this.closeBorder.MouseEnter += closeBorder_MouseEnter;

            this.IsExamEnabled = isExamEnabled;
            this.IsAutoCompletionEnabled = isAutoCompletionEnabled;
            this.IsEnToChineseDictEnabled = isEnToChineseDictEnabled;

            this.PreviewKeyDown += MarkdownEditor_PreviewKeyDown;

            this.editorBase.WordWrap = Globals.MainWindow.TextAutoWrap;

            //处理加密面板布局
            this.setPasswordPanel = new SetPasswordPanel(this);
            this.inputPasswordPanel = new InputPasswordPanel(this);

            this.Content = this.baseGrid;
            this.baseGrid.Children.Add(this.editorBase);
            this.baseGrid.Children.Add(this.setPasswordPanel);
            this.baseGrid.Children.Add(this.InputPasswordPanel);

            //如果文档未加密，直接显示
            if (IsEncripied(this.fullFilePath) == false)
            {
                this.setPasswordPanel.Visibility =
                this.InputPasswordPanel.Visibility = Visibility.Collapsed;
            }
            else
            {
                this.InputPasswordPanel.Visibility = Visibility.Visible;
                this.setPasswordPanel.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// 表示是否元文件的图标。
        /// </summary>
        private Image icon = new Image() { Width = 16, };

        /// <summary>
        /// 展开所有折叠块。
        /// </summary>
        private void MiExpandAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }
        }

        /// <summary>
        /// 折叠所有可折叠的区域。
        /// </summary>
        private void MiCollapseAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }
        }

        /// <summary>
        /// 折叠当前所在的可折叠区域。
        /// </summary>
        private void MiCollapseCurrentFolder_Click(object sender, RoutedEventArgs e)
        {
            this.EditorBase.FoldSelectedBlock();
        }

        /// <summary>
        /// 发送到对照区。LME 不支持同一文档的多视图编辑，故需要对照时借用了右工具栏。
        /// 对照区不可编辑，且不会保持状态，程序关闭就消失。
        /// </summary>
        private void MiSendToCompareArea_Click(object sender, RoutedEventArgs e)
        {
            if (this.inputPasswordPanel.Visibility != Visibility.Collapsed)
            {
                LMessageBox.Show("　　请先输入密码打开文档！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            MarkdownEditor compareEdit = null;
            foreach (UIElement ue in Globals.MainWindow.rightToolBar.Items)
            {
                var edit = ue as MarkdownEditor;
                if (edit == null) continue;
                if (edit.ShortFileName == "对照区[只读]")
                {
                    compareEdit = edit;
                    break;
                }
            }

            if (compareEdit == null)
            {
                compareEdit = new MarkdownEditor("对照区[只读]", false, false, false,
                    Globals.MainWindow.IsShowTabs, Globals.MainWindow.IsShowEndOfLine, Globals.MainWindow.IsShowTabs, true);
                compareEdit.headerPanel.ContextMenu = null;

                foreach (UIElement ue in compareEdit.editorContextMenu.Items)
                {
                    MenuItem mi = ue as MenuItem;
                    if (mi == null)
                    {
                        var s = ue as Separator;
                        if (s != null) s.Visibility = Visibility.Collapsed;
                        continue;
                    }

                    if (mi.Header.ToString() == "复制(_C)")
                    {
                        continue;
                    }
                    else
                    {
                        mi.IsEnabled = false;
                        mi.Visibility = Visibility.Collapsed;
                    }
                }
                compareEdit.EditorBase.IsReadOnly = true;
                compareEdit.EditorBase.FontSize = Globals.MainWindow.mainTabControl.FontSize;
                compareEdit.editorBase.FontFamily = Globals.MainWindow.mainTabControl.FontFamily;
                Globals.MainWindow.rightToolBar.Items.Add(compareEdit);
            }

            compareEdit.EditorBase.WordWrap = this.EditorBase.WordWrap;
            compareEdit.EditorBase.Document.Text = this.editorBase.Text;
            compareEdit.editorBase.IsModified = false;
            Globals.MainWindow.rightToolBar.SelectedItem = compareEdit;

            if (Globals.MainWindow.rightToolBar.Visibility != System.Windows.Visibility.Visible ||
                            Globals.MainWindow.rightToolBar.ActualWidth <= 0)
            {
                Globals.MainWindow.SwitchRightToolBarToggle();
            }
        }

        private void HeaderContextMenu_Opened(object sender, RoutedEventArgs e)
        {
            miPin.IsChecked = BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this);
        }

        /// <summary>
        /// 跳转到引用的 Markdown 文档。
        /// </summary>
        private void MiOpenLinkedMarkdownFile_Click(object sender, RoutedEventArgs e)
        {
            if (GotoAnchorInSameDocument()) return;

            OpenLinkedMarkdownFile();
        }

        /// <summary>
        /// 如果是页面内的锚，直接跳转即可。
        /// </summary>
        public bool GotoAnchorInSameDocument()
        {
            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            Regex regexAnchorRef = new Regex(@"\[.*\]\(\#.{1,}\)");
            MatchCollection matchesOfAnchor = regexAnchorRef.Matches(lineText);
            if (matchesOfAnchor.Count > 0)
            {
                var selOffsetInLine = this.editorBase.SelectionStart - line.Offset;
                foreach (Match match in matchesOfAnchor)
                {
                    if (selOffsetInLine >= match.Index && selOffsetInLine < match.Index + match.Length)
                    {
                        Regex regexAnchorRef2 = new Regex(@"(?<=(\[.*\]\(\#)).{1,}(?=(\)))");
                        var match2 = regexAnchorRef2.Match(match.Value);
                        if (match2.Success)
                        {
                            Regex regexAnchor = new Regex(@"(?<=(\[.*\]\(@))" + match2.Value + @"(?=(\)))");
                            var matchAnchor = regexAnchor.Match(this.editorBase.Document.Text);
                            if (matchAnchor.Success)
                            {
                                var destLine = this.editorBase.Document.GetLineByOffset(matchAnchor.Index);
                                this.editorBase.Select(matchAnchor.Index, matchAnchor.Length);
                                this.editorBase.ScrollTo(destLine.LineNumber, matchAnchor.Index - destLine.Offset);
                                return true;
                            }
                        }

                        return false;
                    }
                }

                return false;
            }

            return false;
        }

        /// <summary>
        /// 跳转到引用的 Markdown 文档。注意：此方法不支持跳转到同一个文档中引用的“锚”。
        /// </summary>
        internal void OpenLinkedMarkdownFile()
        {
            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            var selIndex = this.editorBase.SelectionStart - line.Offset;

            int leftBracketIndex;
            int rightBracketIndex;
            if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
            {
                leftBracketIndex = lineText.IndexOf("](", selIndex);
                rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                if (rightBracketIndex < 0)
                    rightBracketIndex = lineText.IndexOf(")", selIndex);
            }
            else
            {
                leftBracketIndex = lineText.LastIndexOf("](", selIndex);

                if (leftBracketIndex < 0)
                {
                    if (selIndex >= 1)
                    {
                        leftBracketIndex = lineText.LastIndexOf("[", selIndex - 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                    }
                    else
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", selIndex);
                    }
                }
                else
                {
                    rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                    if (rightBracketIndex < 0)
                        rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                }
            }

            if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (File.Exists(this.FullFilePath) == false)
            {
                LMessageBox.Show("　　正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', '　', '\t' }); ;

            var index = relativePath.ToLower().LastIndexOf(".html");

            if (index < 0)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var anchor = relativePath.Substring(index + 5);
            relativePath = relativePath.Substring(0, index) + ".md";//这两行顺序不能倒。

            if (anchor.StartsWith("#"))
            {
                anchor = $"](@{anchor.Substring(1)})";
            }
            else anchor = "";


            var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
            var directory = thisFileInfo.Directory;

            int x = 0;
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                    x++;
                }
                else
                {
                    var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);

                    if (Directory.Exists(subPath) == false)
                    {
                        if (File.Exists(subPath))
                        {
                            //就是目标
                            Globals.MainWindow.OpenDocuments(new string[] { subPath });

                            MarkdownEditor newEditor = null;
                            var newIndexOfDocument = -1;
                            for (int i = 0; i < Globals.MainWindow.mainTabControl.Items.Count; i++)
                            {
                                var editor = Globals.MainWindow.mainTabControl.Items[i] as MarkdownEditor;
                                if (editor != null && editor.FullFilePath.ToLower() == subPath.ToLower())
                                {
                                    newEditor = editor;
                                    newIndexOfDocument = i;
                                    break;
                                }
                            }

                            if (newEditor != null)
                            {
                                Globals.MainWindow.mainTabControl.SelectedIndex = newIndexOfDocument;
                                if (string.IsNullOrEmpty(anchor) == false && anchor.Length > 4)
                                {
                                    var startIndex = newEditor.EditorBase.Text.IndexOf(anchor);
                                    if (startIndex >= 0 && (startIndex + 3) < newEditor.editorBase.Document.TextLength)
                                    {
                                        newEditor.EditorBase.Select(startIndex + 3, anchor.Length - 4);
                                        var destLine = newEditor.EditorBase.Document.GetLineByOffset(startIndex + 3);
                                        newEditor.EditorBase.ScrollTo(destLine.LineNumber, startIndex + 3 - destLine.Offset);
                                    }
                                }
                            }
                            return;
                        }

                        LMessageBox.Show("　　当前插入点位置处的文本不像是个对 Markdown 文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    else
                    {
                        directory = new DirectoryInfo(subPath);
                        x++;
                    }
                }
            }

            if (x != pieces.Length - 1)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对 Markdown 文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
        }

        /// <summary>
        /// 对当前文档进行简单地加密。
        /// </summary>
        private void MiSimpleEncrypt_Click(object sender, RoutedEventArgs e)
        {
            if (File.Exists(this.FullFilePath) == false || this.IsModified)
            {
                LMessageBox.Show("　　此功能需要先保存文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (string.IsNullOrEmpty(this.Password) == false)
            {
                var result = InputBox.Show(Globals.AppName, $"请输入现有密码以便设置新密码！\r\n\r\n提示：{this.PasswordTip}", "", false, null, true);
                if (string.IsNullOrEmpty(result) || result != this.Password)
                {
                    LMessageBox.Show("您输入的密码不正确！无法设置新密码！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }

            setPasswordPanel.Visibility = Visibility.Visible;
            if (InputPasswordPanel.Visibility != Visibility.Collapsed)
            {
                InputPasswordPanel.Visibility = Visibility.Collapsed;
            }
        }

        private string passwordTip = "";
        /// <summary>
        /// 密码提示文本。
        /// </summary>
        public string PasswordTip
        {
            get { return this.passwordTip; }
            set
            {
                this.passwordTip =
                this.InputPasswordPanel.tipBlock.Text = value;
            }
        }

        /// <summary>
        /// 密码文本。
        /// </summary>
        public string Password { get; set; } = "";

        /// <summary>
        /// 首行文本，保存密码、提示问题等信息。
        /// </summary>
        public string EncryptedFirstLine
        {
            get
            {
                if (string.IsNullOrEmpty(this.Password) && string.IsNullOrEmpty(this.PasswordTip))
                {
                    return "";//没有密码。
                }

                var fstLine = new String(SetPasswordPanel.TextEncrypt($"Question:{PasswordTip}|Password:{Password}", "DyBj#PpBb")).Replace("\r", "[[<r>]]").Replace("\n", "[[<n>]]");

                return fstLine;
            }
        }

        /// <summary>
        /// 根据路径判断该文件是否已被加密。
        /// </summary>
        /// <param name="fullFilePath">要检查的 Markdown 文件的完整路径。</param>
        private bool IsEncripied(string fullFilePath)
        {
            if (String.IsNullOrWhiteSpace(fullFilePath) || File.Exists(fullFilePath) == false) return false;

            System.IO.StreamReader s = File.OpenText(fullFilePath);
            var fstEncriptLine = s.ReadLine();
            if (string.IsNullOrEmpty(fstEncriptLine)) return false;

            var fstDecriptedLine = SetPasswordPanel.TextDecrypt(fstEncriptLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
            if (fstDecriptedLine.Contains("Password:") && fstDecriptedLine.Contains("Question:") && fstDecriptedLine.Contains("|"))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 预览引用的图像文件。
        /// </summary>
        private void MiFindLinkImage_Click(object sender, RoutedEventArgs e)
        {
            PreviewLinkedImageFile();
        }

        /// <summary>
        /// 预览引用的图像文件。
        /// </summary>
        internal void PreviewLinkedImageFile()
        {
            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            var selIndex = this.editorBase.SelectionStart - line.Offset;

            int leftBracketIndex;
            int rightBracketIndex;
            if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
            {
                leftBracketIndex = lineText.IndexOf("](", selIndex);
                rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                if (rightBracketIndex < 0)
                    rightBracketIndex = lineText.IndexOf(")", selIndex);
            }
            else
            {
                leftBracketIndex = lineText.LastIndexOf("](", selIndex);

                if (leftBracketIndex < 0)
                {
                    if (selIndex >= 1)
                    {
                        leftBracketIndex = lineText.LastIndexOf("![", selIndex - 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 2);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                    }
                    else
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", selIndex);
                    }
                }
                else
                {
                    rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                    if (rightBracketIndex < 0)
                        rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                }
            }

            if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对图像文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (File.Exists(this.FullFilePath) == false)
            {
                LMessageBox.Show("　　正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', '　', '\t' });

            //if (relativePath.ToLower().EndsWith(".html"))
            //    relativePath = relativePath.Substring(0, relativePath.Length - 4) + ".md";

            var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对图像文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
            var directory = thisFileInfo.Directory;

            int x = 0;
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                    x++;
                }
                else
                {
                    var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);

                    if (Directory.Exists(subPath) == false)
                    {
                        if (WorkspaceTreeViewItem.IsValidateImageFilePath(subPath))
                        {
                            //就是目标
                            ImagePreviewWindow ipw = new ImagePreviewWindow(subPath)
                            {
                                Owner = Globals.MainWindow,
                                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                            };
                            ipw.ShowDialog();
                            return;
                        }

                        LMessageBox.Show("　　当前插入点位置处的文本不像是个对图像文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    else
                    {
                        directory = new DirectoryInfo(subPath);
                        x++;
                    }
                }
            }

            if (x != pieces.Length - 1)
            {
                LMessageBox.Show("　　当前插入点位置处的文本不像是个对图像文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
        }

        private void MiRedo_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Redo();
        }

        private void MiUndo_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Undo();
        }

        private void MiPaste_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Paste();
        }

        private void MiCopy_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Copy();
        }

        private void MiCut_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Cut();
        }

        private bool isCompareAreaEditor = false;
        /// <summary>
        /// 是否对照区。对照区不能双击预览。
        /// </summary>
        public bool IsCompareAreaEditor
        {
            get { return isCompareAreaEditor; }
        }

        /// <summary>
        /// 双击标头编译当前文档并预览。
        /// </summary>
        private void HeaderTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (this.IsCompareAreaEditor) return;
            if (e.ClickCount == 2)
            {
                this.CompileAndPreviewHtml();

                Globals.MainWindow.rightToolBar.SelectedIndex = 0;
                if (Globals.MainWindow.rightToolBar.Visibility != System.Windows.Visibility.Visible ||
                    Globals.MainWindow.rightToolBar.ActualWidth <= 0)
                {
                    Globals.MainWindow.SwitchRightToolBarToggle();
                }
            }
        }

        /// <summary>
        /// 编译并调用外部浏览器预览。
        /// </summary>
        private void MiCallSystemDefaultWebBrowserPreview_Click(object sender, RoutedEventArgs e)
        {
            this.CompileAndPreviewHtml(true);
        }

        /// <summary>
        /// 编译当前 Markdown 文档并在右工具栏中预览。
        /// </summary>
        void miCompileAndPreview_Click(object sender, RoutedEventArgs e)
        {
            this.CompileAndPreviewHtml();
        }

        void closeBorder_MouseEnter(object sender, MouseEventArgs e)
        {
            this.closeBorder.Background = Brushes.LightGray;
        }

        void closeBorder_MouseLeave(object sender, MouseEventArgs e)
        {
            this.closeBorder.Background = Brushes.Transparent;
        }

        /// <summary>
        /// 向编辑区拖入图像文件时，自动复制到图像资源文件夹并自动生成链接。
        /// </summary>
        void MarkdownEditor_Drop(object sender, DragEventArgs e)
        {
            Array dragDataArray = ((System.Array)e.Data.GetData(DataFormats.FileDrop));
            if (dragDataArray == null || dragDataArray.Length <= 0) return;

            var filenames = new System.Collections.Specialized.StringCollection();

            foreach (string s in dragDataArray)
            {
                filenames.Add(s);
            }

            if (TryToDropImages(filenames) == string.Empty) return;
        }

        void MarkdownEditor_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            bool isCtrl = false;
            if ((Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            bool isShift = false;
            if ((Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            bool isAlt = false;
            if ((Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Down) > 0)
            {
                isAlt = true;
            }

            switch (e.Key)
            {
                case Key.V:
                    {
                        if (isCtrl && isAlt == false)
                        {
                            if (isShift)
                            {
                                //粘贴代码
                                PasteCode();
                            }
                            else
                            {
                                if (string.IsNullOrEmpty(Clipboard.GetText()) == false)
                                {
                                    this.editorBase.Paste();
                                }
                                else
                                {
                                    //如果是粘贴图像，粘贴后不再继续操作。
                                    var result = TryToPasteImage();
                                    if (result == string.Empty)
                                    {
                                        e.Handled = true;
                                        break;
                                    }

                                    LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                                }
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F4:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            Close();
                            e.Handled = true;
                        }
                        else
                        {
                            if (!isShift && !isAlt)
                            {
                                //单F4，生成选择题标签集
                                if (IsExamEnabled) editorBase.InsertChoiceQuestionTags();
                            }
                        }
                        break;
                    }
                case Key.W:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            Close();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F2:
                    {
                        if (isCtrl == false && isShift == false && isAlt == false)
                        {
                            if (this.EditorBase.SelectionLength <= 0)
                            {
                                //如果没选中文本，说明是打算输入一个新填空项。
                                //这时候直接定位到两个括号中间好。
                                this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
                                this.editorBase.Select(this.editorBase.SelectionStart + 1, 0);
                            }
                            else
                            {
                                //如果已经选定了一些文本，说明是打算将这些文本作为一个填空项，
                                //此时一般没必要再修改文本，直接定位到右括弧后面，可以接着输入，这样更方便些。
                                this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
                                this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                            }
                        }
                        break;
                    }
                case Key.F8:
                    {
                        if (!isCtrl && !isAlt && !isShift)
                        {
                            if (IsExamEnabled) this.editorBase.InsertJudgeQuestionTags("正确");
                        }
                        break;
                    }
                case Key.F9:
                    {
                        if (!isCtrl && !isAlt && !isShift)
                        {
                            if (IsExamEnabled) this.editorBase.InsertJudgeQuestionTags("错误");
                        }
                        break;
                    }
                case Key.P:
                    {
                        if (isCtrl && !isAlt)
                        {
                            if (isShift)
                            {
                                Globals.MainWindow.PrintPreview();
                            }
                            else
                            {
                                Globals.MainWindow.PrintDocument();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F:
                    {
                        if (isCtrl)
                        {
                            if (isAlt)
                            {
                                //Ctrl+Alt+F
                                if (Globals.MainWindow.rdFindAndReplace.ActualHeight <= 20)
                                {
                                    Globals.MainWindow.rdFindAndReplace.Height = new GridLength(0, GridUnitType.Auto);
                                    Globals.MainWindow.cmbFindText.UpdateLayout();
                                }

                                var editor = Globals.MainWindow.ActivedEditor;
                                if (editor != null)
                                {
                                    Globals.MainWindow.cmbFindText.Text = editor.EditorBase.SelectedText;
                                }
                                else return;

                                Globals.MainWindow.cmbSearchArea.SelectedIndex = 1;

                                Globals.MainWindow.cmbFindText.Focus();

                                if (Globals.MainWindow.cmbFindText.Text.Length > 0)
                                {
                                    Globals.MainWindow.FindText(Globals.MainWindow.tvFindAndReplace);
                                }

                                Globals.MainWindow.cmbFindText.Focus();

                                if (Globals.MainWindow.cdRightToolsArea.ActualWidth < 140)
                                {
                                    Globals.MainWindow.cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
                                }
                                if (Globals.MainWindow.rightToolBar.SelectedIndex != 1)
                                {
                                    Globals.MainWindow.rightToolBar.SelectedIndex = 1;
                                }

                                e.Handled = true;
                            }
                            else
                            {
                                //Ctrl+F                                    
                                //miFindInActiveDocument_Click(sender, e);
                                if (Globals.MainWindow.rdFindAndReplace.ActualHeight <= 20)
                                {
                                    Globals.MainWindow.rdFindAndReplace.Height = new GridLength(0, GridUnitType.Auto);
                                    Globals.MainWindow.cmbFindText.UpdateLayout();
                                }

                                var editor = Globals.MainWindow.ActivedEditor;
                                if (editor != null)
                                {
                                    Globals.MainWindow.cmbFindText.Text = editor.EditorBase.SelectedText;
                                }
                                else return;

                                Globals.MainWindow.cmbSearchArea.SelectedIndex = 0;

                                Globals.MainWindow.cmbFindText.Focus();
                                if (Globals.MainWindow.cmbFindText.Text.Length > 0)
                                {
                                    Globals.MainWindow.Find();
                                }

                                e.Handled = true;
                            }
                        }
                        break;
                    }
            }
        }

        /// <summary>
        /// 向编辑区拖入图像文件时，自动尝试复制图像文件到资源目录下并自动生成链接文本。
        /// </summary>
        /// <param name="fileDropList">拖动的图像文件的列表。</param>
        public string TryToDropImages(System.Collections.Specialized.StringCollection fileDropList)
        {
            //try
            //{
            if (fileDropList.Count > 0)
            {
                foreach (string fileFullPath in fileDropList)
                {
                    if (WorkspaceTreeViewItem.IsValidateImageFilePath(fileFullPath) == false) continue;

                    if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
                    {
                        Directory.CreateDirectory(this.ImageResourceDirectoryPath);
                    }

                    var shortImageFileInfo = new FileInfo(fileFullPath);
                    var shortImageFileName = shortImageFileInfo.Name;
                    var destFilePath = this.ImageResourceDirectoryPath + shortImageFileName;

                    if (fileFullPath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()))
                    {
                        //如果图像文件就在工作区内，则生成相对引用链接而不是复制图像文件。

                        var srcPath = fileFullPath.ToLower();
                        var workspacePath = Globals.PathOfWorkspace.ToLower();

                        //思路：先找出共同的、最接近的祖先级目录
                        var separator = new char[] { '\\', '/' };
                        string[] src = srcPath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                        string[] dest = destFilePath.ToLower().Split(separator, StringSplitOptions.RemoveEmptyEntries);

                        var minIndex = Math.Min(src.Length, dest.Length) - 1;

                        if (minIndex < 0) return string.Empty;

                        int aIndex = -1;
                        for (int i = 0; i <= minIndex; i++)
                        {
                            if (src[i] != dest[i])
                            {
                                aIndex = i;
                                break;
                            }
                        }

                        //生成相对路径的前半部分，类似（../../）这样子。

                        string[] file = this.FullFilePath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                        string header;
                        string tail;
                        if (aIndex < 0)
                        {
                            header = "";//就已经在同一个文件的资源目录中
                            tail = (shortFileName.ToLower().EndsWith(".md") ? shortFileName.Substring(0, shortFileName.Length - 3) : shortFileName)
                                + "~/Images~/" + shortImageFileName;
                        }
                        else
                        {
                            header = BuildHtmlRefText(file.Length - aIndex - 1);
                            //生成相对路径的后半部分。
                            StringBuilder sb = new StringBuilder();
                            for (int i = aIndex; i < src.Length; i++)
                            {
                                sb.Append(src[i] + "/");
                            }

                            tail = sb.ToString();
                            if (tail.EndsWith("/"))
                            {
                                tail = tail.Substring(0, tail.Length - 1);
                            }
                        }

                        //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='" + header + tail + "' />";

                        var shortTitle = MainWindow.FormatDocumentTitle(shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length));
                        if (fileDropList.Count == 1)
                        {
                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                        }
                        else
                        {
                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")\r\n";
                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                        }
                    }
                    else
                    {
                        //如果不在工作区内，则直接将文件复制到工作区内对应资源文件目录。
                        if (File.Exists(destFilePath) == false)
                        {
                            File.Copy(fileFullPath, destFilePath);
                        }

                        //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + "' />";

                        var shortTitle = shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length);
                        if (fileDropList.Count == 1)//不然会出错。
                        {
                            this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ")";
                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                        }
                        else
                        {
                            this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ")\r\n";
                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                        }
                    }
                }

                //在资源管理器界面进行刷新。
                Globals.MainWindow.WorkspaceManager.Refresh(null);
            }
            else
            {
                return "没有图像文件可以拖放。";
            }
            //}
            //catch (Exception ex)
            //{
            //    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            //    return ex.Message;
            //}

            return string.Empty;
        }

        /// <summary>
        /// 创建引用的相对路径字符串前缀。
        /// </summary>
        /// <param name="layer">相对层级差。</param>
        private string BuildHtmlRefText(int layer)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < layer; i++)
            {
                sb.Append("../");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 尝试向当前编辑器粘贴图像数据块。
        /// 粘贴时会要求用户给图像起个名称，然后快速保存到当前文档的图像资源文件夹中，
        /// 并自动生成对该文件的链接文本。
        /// * 注意：如果 Windows 系统的剪贴板被其它程序占用，会无法粘贴。
        /// </summary>
        private string TryToPasteImage()
        {
            WinForm.IDataObject oDataObj = WinForm.Clipboard.GetDataObject();

            if (oDataObj != null && System.Windows.Clipboard.ContainsData(DataFormats.Bitmap))
            {
                var shortImageFileName = InputBox.Show(Globals.AppName, "请输入图像文件名（不要后缀名）：", "", true);
                if (string.IsNullOrEmpty(shortImageFileName)) return "用户未输入文件名。";

                try
                {
                    System.Drawing.Image oImgObj = oDataObj.GetData(DataFormats.Bitmap, true) as System.Drawing.Image;
                    if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
                    {
                        Directory.CreateDirectory(this.ImageResourceDirectoryPath);
                    }
                    var destFilePath = this.ImageResourceDirectoryPath + shortImageFileName + ".png";

                    if (File.Exists(destFilePath))
                    {
                        var result = LMessageBox.Show("　　同名文件已经存在，继续操作会覆盖原文件。\r\n\r\n　　要继续吗？",
                            Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning);
                        if (result != MessageBoxResult.Yes)
                        {
                            return "用户放弃操作！";
                        }
                    }

                    oImgObj.Save(destFilePath, System.Drawing.Imaging.ImageFormat.Png);
                    //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' width='" + oImgObj.Width + "px' height='" + oImgObj.Height +
                    //"px' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png' />";
                    this.editorBase.SelectedText = "![" + shortImageFileName + "](" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png)";

                    WinForm.Clipboard.Clear();//否则下次复制、粘贴文本时总会弹出一个对话框要求设置要粘贴的图像的文件名。

                    //在资源管理器界面进行刷新。
                    Globals.MainWindow.WorkspaceManager.Refresh(null);
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
            else
            {
                return TryToDropImages(Clipboard.GetFileDropList());
            }

            return string.Empty;
        }

        /// <summary>
        /// 当前 Markdown 文件对应的资源文件夹的短名称。
        /// </summary>
        public string ShortResourceDirectoryPath
        {
            get
            {
                var shortFileName = this.ShortFileName.ToLower();
                if (string.IsNullOrEmpty(shortFileName)) return string.Empty;

                if (shortFileName.EndsWith(".md")) return shortFileName.Substring(0, shortFileName.Length - 3) + "~";
                return string.Empty;
            }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的资源文件夹的完整路径。
        /// </summary>
        public string ResourceDirectoryPath
        {
            get
            {
                if (File.Exists(this.FullFilePath) == false) return string.Empty;
                return this.FullFilePath.Substring(0, this.fullFilePath.Length - 3) + "~\\";
            }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的图像资源文件夹的完整路径。
        /// </summary>
        public string ImageResourceDirectoryPath
        {
            get
            {
                var resourceDirectoryPath = this.ResourceDirectoryPath;
                if (string.IsNullOrEmpty(resourceDirectoryPath))
                {
                    return string.Empty;
                }

                return resourceDirectoryPath + "Images~\\";
            }
        }

        /// <summary>
        /// 文档被修改状态变化时，更改编辑器标头状态字符。
        /// </summary>
        void UndoStack_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            //用于判断文档是否已被修改。
            if (e.PropertyName == "IsOriginalFile")
            {
                modifiedTextBlock.Text = editorBase.Document.UndoStack.IsOriginalFile ? string.Empty : "*";
            }
        }

        private string htmlDocumentPageHeader = string.Empty;
        /// <summary>
        /// Html 页面标题文本
        /// </summary>
        public string HtmlDocumentPageHeader
        {
            get
            {
                if (string.IsNullOrEmpty(this.htmlDocumentPageHeader))
                {
                    return string.Empty;
                }
                return this.htmlDocumentPageHeader;
            }
        }

        private string htmlDocumentTitle = string.Empty;
        /// <summary>
        /// 编译后的html文档的标题，在MarkDown文档中以%（或%）顶格开头。
        /// </summary>
        public string HtmlDocumentTitle
        {
            get
            {
                //if (string.IsNullOrEmpty(this.editorBase.Text)) return string.Empty;

                //string[] lines = this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                //foreach (var line in lines)
                //{
                //    char[] trimStartChars = new char[] { '\t', ' ', '　' };
                //    var s = line.TrimStart(trimStartChars);

                //    if (s.StartsWith("%") || s.StartsWith("%"))
                //    {
                //        if (s.Length >= 2)
                //        {
                //            if (s[1] != '%' && s[1] != '%')
                //                return s.Substring(1).Replace("%", "").Replace("%", "").Replace("\t", "").TrimEnd(new char[] { '\t', ' ', '　' });
                //        }
                //    }
                //}
                //return string.Empty;
                if (string.IsNullOrEmpty(this.htmlDocumentTitle))
                {
                    return "Markdown Document";
                }
                return this.htmlDocumentTitle;
            }
        }

        private string footerText = string.Empty;
        /// <summary>
        /// 编译后的html文本的脚注文本，在MarkDown文档档中以@(或·）顶格开头。
        /// </summary>
        public string FooterText
        {
            get { return footerText; }
        }

        private bool isExamEnabled = false;
        /// <summary>
        /// 是否启用试题编辑功能。（依赖“自动完成”功能）
        /// </summary>
        public bool IsExamEnabled
        {
            get
            {
                return isExamEnabled;
            }

            set
            {
                isExamEnabled = value;
                this.editorBase.IsExamEnabled = isExamEnabled;
            }
        }

        private bool isAutoCompletionEnabled = false;
        /// <summary>
        /// 是否启用“自动完成”功能。
        /// </summary>
        public bool IsAutoCompletionEnabled
        {
            get { return isAutoCompletionEnabled; }
            set
            {
                isAutoCompletionEnabled = value;
                this.editorBase.IsAutoCompletionEnabled = value;
            }
        }

        private bool isEnToChineseDictEnabled = false;
        /// <summary>
        /// 是否启用英译中词条自动提示功能。（依赖“自动完成”功能）
        /// </summary>
        public bool IsEnToChineseDictEnabled
        {
            get { return isEnToChineseDictEnabled; }
            set
            {
                isEnToChineseDictEnabled = value;
                this.editorBase.IsEnToChineseDictEnabled = value;
            }
        }

        private bool isShowSpaces = false;
        /// <summary>
        /// 决定是否显示空格。
        /// </summary>
        public bool IsShowSpaces
        {
            get { return isShowSpaces; }
            set
            {
                isShowSpaces = value;
                this.editorBase.IsShowSpaces = value;
            }
        }

        private bool isShowEndOfLine = false;
        /// <summary>
        /// 决定是否显示段落标记。
        /// </summary>
        public bool IsShowEndOfLine
        {
            get { return isShowEndOfLine; }
            set
            {
                isShowEndOfLine = value;
                this.editorBase.IsShowEndOfLine = value;
            }
        }

        private bool isShowTabs = false;
        /// <summary>
        /// 决定是否显示空格。
        /// </summary>
        public bool IsShowTabs
        {
            get { return isShowTabs; }
            set
            {
                isShowTabs = value;
                this.editorBase.IsShowTabs = value;
            }
        }

        /// <summary>
        /// 编译当前编辑器中的 Markdown 文本并预览。
        /// </summary>
        /// <param name="callSystemDefaultWebBrowser">是否调用 Windows 系统默认浏览器预览。</param>
        public void CompileAndPreviewHtml(bool callSystemDefaultWebBrowser = false)
        {
            if (File.Exists(this.fullFilePath) == false || this.editorBase.IsModified)
            {
                var result = LMessageBox.Show("　　文件需要先保存才可以预览！要保存吗？", Globals.AppName,
                    MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
                if (result != MessageBoxResult.OK)
                {
                    this.editorBase.Focus();
                    return;
                }
                else
                {
                    var saveresult = this.SaveDocument();
                    if (saveresult != string.Empty)
                    {
                        LMessageBox.Show(saveresult, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                }
            }

            if (this.fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
            {
                LMessageBox.Show("　　只有在默认工作区目录下的文件才可以预览！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                this.editorBase.Focus();
                return;
            }

            string htmlTitle = "";

            string newFilePath = CustomMarkdownSupport.Compile(this.fullFilePath,
                Globals.MainWindow.HtmlHeadersCollapse, ref htmlTitle);
            if (File.Exists(newFilePath))
            {
                //this.webBrowser.NavigateToString(htmlDocumentText);
                //WinForm内置的WebBrowser总是出现“JSON未定义错误”，而现代浏览器则不会，所以直接调用外部浏览器好了。

                //System.Diagnostics.Process.Start(newFilePath);

                var destUri = new Uri(newFilePath);//不能加这个前缀

                if (callSystemDefaultWebBrowser == false)
                {
                    if (Globals.MainWindow.previewFrame.Source == null ||
                        Globals.MainWindow.previewFrame.Source.AbsolutePath != destUri.AbsolutePath)
                    {
                        Globals.MainWindow.previewFrame.Source = destUri;
                    }
                    else Globals.MainWindow.previewFrame.Refresh();

                    Globals.MainWindow.rightToolBar.SelectedItem = Globals.MainWindow.tiHtmlPreview;

                    Globals.MainWindow.rightToolBar.SelectedIndex = 0;
                    if (Globals.MainWindow.rightToolBar.Visibility != System.Windows.Visibility.Visible ||
                        Globals.MainWindow.rightToolBar.ActualWidth <= 0)
                    {
                        Globals.MainWindow.SwitchRightToolBarToggle();
                    }

                    this.editorBase.Focus();
                }
                else
                {
                    System.Diagnostics.Process.Start("explorer.exe", $"\"{newFilePath}\"");
                    //不能用destUri.AbsoluteUri，因为会进行URL转码导致找不到文件;
                    //不能加这个前缀"file:///" + 
                }
            }
        }

        /// <summary>
        /// 关闭编辑器。
        /// </summary>
        void closeImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 关闭编辑器。
        /// </summary>
        private void Close()
        {
            if (this.IsModified)
            {
                MessageBoxResult r = LMessageBox.Show("　　文档尚未保存，要先保存文件吗？",
                    Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Exclamation);
                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            //保存
                            var saveResult = SaveDocument();
                            if (string.Empty != saveResult)
                            {
                                LMessageBox.Show(saveResult, Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Error);
                                return;
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            //不保存
                            break;
                        }
                    case MessageBoxResult.Cancel: return;
                }
            }

            //关闭文档
            BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
            if (parentTabControl == null) return;

            if (parentTabControl.Items.Contains(this))
            {
                parentTabControl.Items.Remove(this);
            }
        }

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static MarkdownEditor()
        {
            closeImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CloseHS.png"));
            closeBorderMargin = new Thickness(4, 0, 0, 0);
        }

        private static Thickness closeBorderMargin;

        private static BitmapImage closeImageSource;

        private StackPanel headerPanel = new StackPanel() { Orientation = Orientation.Horizontal, };
        /// <summary>
        /// 标头上用来显示文件短名的文本块。
        /// </summary>
        private TextBlock headerTextBlock = new TextBlock()
        {
            MaxWidth = 100,
            FontSize = 14,
            VerticalAlignment = VerticalAlignment.Center,
            FontFamily = new FontFamily("Simsun"),
        };

        /// <summary>
        /// 标头上用以显示文件是否被修改的状态的文本块。
        /// </summary>
        private TextBlock modifiedTextBlock = new TextBlock() { FontSize = 16, };

        private Image closeImage = new Image() { Width = 16, Height = 24, };

        private Border closeBorder = new Border() { Margin = closeBorderMargin, ToolTip = "关闭[Ctrl+W | Ctrl+F4]", };

        /// <summary>
        /// 关闭文档。
        /// </summary>
        void miCloseDocument_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            if (this.IsModified)
            {
                MessageBoxResult r = LMessageBox.Show("　　当前文档已修改，要保存吗？", Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            string saveInfo = SaveDocument();
                            if (saveInfo == string.Empty)
                            {
                                BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                                if (parentTabControl != null)
                                {
                                    if (parentTabControl.Items.Contains(this))
                                    {
                                        parentTabControl.Items.Remove(this);
                                    }
                                }
                            }
                            else if (saveInfo == "用户取消操作")
                            {
                                return;//不关闭。
                            }
                            else
                            {
                                LMessageBox.Show("　　未能保存文件！错误消息如下：\r\n" + saveInfo);
                            }
                            return;
                        }
                    case MessageBoxResult.No:
                        {
                            BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                            if (parentTabControl != null)
                            {
                                if (parentTabControl.Items.Contains(this))
                                {
                                    parentTabControl.Items.Remove(this);
                                }
                            }
                            return;
                        }
                    default://Cancel
                        {
                            return;
                        }
                }
            }
            else
            {
                BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                if (parentTabControl != null)
                {
                    if (parentTabControl.Items.Contains(this))
                    {
                        parentTabControl.Items.Remove(this);
                    }
                }
            }
        }

        /// <summary>
        /// 格式化 Markdown 文本。
        /// </summary>
        public string FormatedMarkdownText()
        {
            return CustomMarkdownSupport.FormatMarkdownText(this.editorBase.Text);
        }

        /// <summary>
        /// 格式化二维文字表。
        /// </summary>
        public void FormatTextTable()
        {
            this.editorBase.FormatTextTable();
        }

        /// <summary>
        /// 保存文档。顺利完成就返回 string.Empth。否则返回错误消息。
        /// </summary>
        public string SaveDocument()
        {
            if (string.IsNullOrEmpty(this.fullFilePath))
            {
                return SaveDocumentAs();
            }

            try
            {
                var fstLine = EncryptedFirstLine;
                if (string.IsNullOrEmpty(fstLine))
                {
                    this.editorBase.Save(this.fullFilePath);//如果没有密码，则不加密。
                }
                else
                {
                    File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(this.editorBase.Document.Text, "DyBj#PpBb"))}");
                }

                this.editorBase.IsModified = false;//设置保存状态。
                OnSaved();
                return string.Empty;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 文档另存为。顺利完成就返回 string.Empth。否则返回错误消息。
        /// </summary>
        public string SaveDocumentAs()
        {
            try
            {
                SaveFileDialog sfd = new SaveFileDialog() { Title = Globals.AppName, };
                sfd.Filter = Globals.DocumentName +
                "(*." + Globals.ExtensionName + ")|*." + Globals.ExtensionName;
                sfd.FilterIndex = 0;
                sfd.FileName = this.shortFileName;
                sfd.InitialDirectory = Globals.PathOfWorkspace;

                if (sfd.ShowDialog() == true)
                {
                    if (sfd.FileName.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
                    {
                        var reslut = LMessageBox.Show("　　只有保存在默认工作区目录下才可以预览！真的要继续吗？", Globals.AppName,
                            MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);

                        if (reslut != MessageBoxResult.OK) return "用户放弃保存。";
                    }

                    var fstLine = EncryptedFirstLine;
                    if (string.IsNullOrEmpty(fstLine))
                    {
                        this.editorBase.Save(this.fullFilePath);//要考虑加密，不能直接用这个
                    }
                    else
                    {
                        File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(this.editorBase.Document.Text, "DyBj#PpBb"))}");
                    }

                    this.FullFilePath = sfd.FileName;//更新文件路径。
                    this.editorBase.IsModified = false;//设置保存状态。
                    OnSaved();
                    return string.Empty;
                }
                else
                {
                    return "用户放弃保存";
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        public event EventHandler<DocumentSavedEventArgs> Saved;

        protected void OnSaved()
        {
            string title = null;
            foreach (var line in this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
            {

                if (line.StartsWith("%"))
                {
                    title = line.Substring(1).Trim();
                    break;
                }

                title = CustomMarkdownSupport.GetDocumentTitle(line);
                if (string.IsNullOrWhiteSpace(title) == false) break;

                int startIndex = line.IndexOf("标题＞＞");
                int endIndex = line.IndexOf("＜＜标题");
                if (startIndex >= 0 && endIndex >= 0)
                {
                    title = line.Substring(startIndex + 4, endIndex - startIndex - 4);
                    break;
                }
            }

            if (string.IsNullOrWhiteSpace(title) == false)
            {
                this.headerTextBlock.Text = title;
            }
            else
            {
                this.headerTextBlock.Text = this.ShortFileName;
            }

            if (Saved != null)
            {
                Saved(this, new DocumentSavedEventArgs() { FileFullName = this.FullFilePath, });
            }
        }

        void miPin_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            BetterWpfControls.Panels.ScrollablePanel.SetIsLocked(this, !BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this));
        }

        /// <summary>
        /// 打开指定路径的 Markdown 文件。
        /// </summary>
        /// <param name="fullPath">要打开的 Markdown 文件的路径。</param>
        /// <returns>顺利完成就返回 string.Empty。</returns>
        internal string OpenDocument(string fullPath)
        {
            if (IsEncripied(fullPath))
            {
                this.ShortFileName = new FileInfo(fullPath).Name;
                this.FullFilePath = fullPath;

                var fstLine = new StreamReader(fullFilePath).ReadLine();

                var decryptedFstLine = SetPasswordPanel.TextDecrypt(
                    fstLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
                var indexOfPassword = decryptedFstLine.IndexOf("Password:");
                var indexOfQuestion = decryptedFstLine.IndexOf("Question:");
                if (indexOfQuestion >= 0 && indexOfPassword >= 10)
                {
                    this.Password = decryptedFstLine.Substring(indexOfPassword + 9);
                    this.PasswordTip = decryptedFstLine.Substring(9, indexOfPassword - 10);

                    this.InputPasswordPanel.Visibility = Visibility.Visible;
                    if (this.setPasswordPanel.Visibility != Visibility.Collapsed)
                    {
                        this.setPasswordPanel.Visibility = Visibility.Collapsed;
                    }
                    return string.Empty;
                }
            }

            if (string.IsNullOrEmpty(fullPath)) return "未提供有效文件名\r\n";
            if (System.IO.File.Exists(fullPath) == false) return string.Format("　　不存在路径为{0}的文件\r\n", fullPath);

            this.FullFilePath = fullPath;
            this.EditorBase.Load(this.FullFilePath);

            //初始化文件保存状态。
            this.EditorBase.IsModified = false;

            return string.Empty;
        }

        /// <summary>
        /// 粘贴图像成链接或粘贴文本。
        /// </summary>
        internal void Paste()
        {
            //如果是粘贴图像，粘贴后不再继续操作。
            var result = TryToPasteImage();
            if (result == string.Empty) return;

            var clipboardText = Clipboard.GetText();
            if (string.IsNullOrEmpty(clipboardText) == false)
            {
                this.editorBase.Paste();
            }
            else
            {
                LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// 行分割符，根据操作系统不同而可能不同。这里只是以防万一。
        /// </summary>
        public string Splitter
        {
            get
            {
                string splitter;
                if (this.editorBase.Text.Contains("\r\n"))
                {
                    splitter = "\r\n";
                }
                else if (this.editorBase.Text.Contains("\n"))
                {
                    splitter = "\n";
                }
                else
                {
                    splitter = "\r";
                }
                return splitter;
            }
        }

        /// <summary>
        /// 粘贴源代码。会自动为粘贴的源代码添加行号、行首空格。
        /// </summary>
        internal void PasteCode()
        {
            var clipboardText = Clipboard.GetText().Replace("\t", "    ");
            if (string.IsNullOrEmpty(clipboardText)) return;

            string[] lines = clipboardText.Replace("\r", "").Split(new char[] { '\n', }, StringSplitOptions.None);
            if (lines.Length <= 0)
            {
                this.editorBase.SelectedText = "";
                return;
            }

            bool allStartsWithFourSpace = true;
            foreach (var line in lines)
            {
                if (line.StartsWith("    ") == false) allStartsWithFourSpace = false;
            }

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < lines.Length; i++)
            {
                string line;
                if (allStartsWithFourSpace)
                {
                    line = lines[i].Substring(4);
                }
                else
                {
                    line = lines[i];
                }

                string s = "    " + (i + 1).ToString("D2") + "." + "\t" + line;

                sb.Append(s + Splitter);
            }

            this.editorBase.SelectedText = Splitter + sb.ToString() + Splitter;
        }
    }

    /// <summary>
    /// 此类用于文件被保存时传递文件路径。
    /// </summary>
    public class DocumentSavedEventArgs : EventArgs
    {
        public string FileFullName { get; set; }
    }
}
